#include <sys/epoll.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "ae.h"


static int lcreate(lua_State* L)
{
    int aefd = ae_create();
    lua_pushinteger(L,aefd);
    return 1;
}

static int lclose(lua_State* L)
{
    int aefd = luaL_checkinteger(L,1);
    ae_free(aefd);
    return 0;
}

static int ladd_read(lua_State* L)
{
    int aefd = luaL_checkinteger(L,1);
    int fd = luaL_checkinteger(L,2);
    int ret = ae_add_read(aefd,fd);
    if(ret != 1)
    {
        return luaL_error(L,"add read event failed");
    }
    lua_pushinteger(L,0);
    return 1;
}

static int ladd_write(lua_State* L)
{
    int aefd = luaL_checkinteger(L,1);
    int fd = luaL_checkinteger(L,2);
    int ret = ae_add_write(aefd,fd);
    if(ret != 1)
    {
        return luaL_error(L,"add read event failed");
    }
    lua_pushinteger(L,0);
    return 1;
}

static int lenable(lua_State* L)
{
    int aefd = luaL_checkinteger(L,1);
    int fd = luaL_checkinteger(L,2);
    bool readable = lua_toboolean(L,3);
    bool writeable = lua_toboolean(L,4);
    int ret = ae_enable_event(aefd,fd,readable,writeable);
    if(ret != 1)
    {
        return luaL_error(L,"add enable event failed");
    }
    lua_pushinteger(L,0);
    return 1;
}

static int ldel(lua_State* L)
{
    int aefd = luaL_checkinteger(L,1);
    int fd = luaL_checkinteger(L,2);
    ae_del_event(aefd,fd);
    return 0;
}

static int poll_callback(void* l,int ev_handle, int aefd, int fd, bool readable,bool writable, int evtype)
{
    lua_State* L = (lua_State*)l;
    lua_rawgeti(L,LUA_REGISTRYINDEX, ev_handle);
    lua_pushinteger(L,fd);
    lua_pushboolean(L,readable);
    lua_pushboolean(L,writable);
    if((evtype&EPOLLOUT)!=0)//避免可写事件无效触发
    {
        //ae_enable_event(aefd,fd,(evtype&EPOLLIN)!=0,false);
    }
    if(lua_pcall(L, 3, 0, 0) != 0)
    {
        const char* error = lua_tostring(L, -1);
        printf("fd=%d, readable=%d, writable=%d, event handler error: %s.\n",fd,readable,writable,error);
        return 1;
    }
    
    return 0;
}

static int lpoll(lua_State* L)
{
    int aefd = luaL_checkinteger(L,1);
    int timeoutMs = luaL_checkinteger(L,2);
    // lua_handler luaL_checkfunction(L,3) // void(*func)(int fd,bool readable,bool writeable);
    /*
    event_t events[AE_MAXEVENT];
    int ret = ae_poll(aefd,events,AE_MAXEVENT,timeoutMs);
    if(ret<=0)
        return -1;
    int lua_ev_handle_ref = luaL_ref(L,LUA_REGISTRYINDEX);
    for(int i=0;i<ret&&lua_ev_handle_ref!=LUA_REFNIL;i++)
    {
        lua_rawgeti(L,LUA_REGISTRYINDEX, lua_ev_handle_ref);
        lua_pushinteger(L,events[i].fd);
        lua_pushboolean(L,events[i].read);
        lua_pushboolean(L,events[i].write);
        if(lua_pcall(L, 3, 0, 0) != 0)
        {
            const char* error = lua_tostring(L, -1);
	    printf("fd=%d, readable=%d, writable=%d, event handler error: %s.\n",events[i].fd,events[i].read,events[i].write,error);
	    break;
        }
        //else printf("fd=%d, readable=%d, writable=%d, event handler success.\n",events[i].fd,events[i].read,events[i].write);
        lua_ev_handle_ref = luaL_ref(L,LUA_REGISTRYINDEX);
    }
    if(lua_ev_handle_ref!=LUA_REFNIL)
        luaL_unref(L, LUA_REGISTRYINDEX,lua_ev_handle_ref);
    return 0;
    */
    int lua_ev_handle_ref = luaL_ref(L,LUA_REGISTRYINDEX);
    if(lua_ev_handle_ref==LUA_REFNIL)
    {
        lua_pushstring(L,"ev_handle_ref failed.");
        return 1;
    }
    int ret = ae_poll2(aefd,timeoutMs,poll_callback,L,lua_ev_handle_ref);
    if(lua_ev_handle_ref!=LUA_REFNIL)
        luaL_unref(L, LUA_REGISTRYINDEX, lua_ev_handle_ref);
    lua_pushinteger(L,ret);
    return 1;
}

static int lwait(lua_State* L)
{
    int fd = luaL_checkinteger(L,1);
    int mask = luaL_checkinteger(L,2);
    int timeoutMs = luaL_checkinteger(L,3);
    int ret = ae_wait(fd,mask,timeoutMs);
    lua_pushinteger(L,ret);
    return 1;
}

static const struct luaL_Reg libae[]=
{
    {"create",lcreate},
    {"close",lclose},
    {"add_write",ladd_write},
    {"add_read",ladd_read},
    {"enable",lenable},
    {"del",ldel},
    {"poll",lpoll},
    {"wait",lwait},
    {NULL,NULL}
};

// lua statemant 'local ae = require "cosocket.ae"' will call "luaopen_cosocket_ae" function
int luaopen_cosocket_ae(lua_State* L)
{
    //luaL_newlib(L,libae);// lua5.3
    luaL_openlib(L,"ae",libae,0);
    return 1;
}








































